

import { Terminal } from "xterm";
import { IProcessModule } from "../Process";

type ByteBuffer = Uint8Array;

interface VirtualFileSystem {
    [path: string]: ByteBuffer | null | undefined; // TODO: do right
}

interface Environment {
    fs: VirtualFileSystem;
    cwd: string;
}

class VirtualMachine {
    public constructor(private fs: VirtualFileSystem, private process: IProcessModule) {

    }

    private syscall(origin: Worker, func: string, arg: any): void {
        switch (func) {
            case "stdout":
                // TODO:
                this.process.stdout.write(arg);
                //document.getElementById("stdout")!.textContent += arg;
                break;
            case "stderr":
                //this.process.stdout.write(arg);
                //document.getElementById("stderr")!.textContent += arg;
                break;
            case "error":
                //this.process.stdout.write("[Runtime Error]\n");
                //this.process.stdout.write(arg + "\n");
                //if (arg.stack)
                //    this.process.stdout.write(arg.stack + "\n");
                break;
            // case "__trace.fs":
            // case "__trace.require":
            //   break;
            // case "__trace.fs":
            //   console.log(JSON.stringify(arg, null, 2));
            //   break;
            case "WRITE":
                this.fs[arg.path] = arg.content;
                break;
            case "EXIT":
                const exitCode = arg;
                origin.terminate();
                break;
        }
    }

    /**
     * Dummy entry point for "node" binary. Long term, this should be hooked into the FS somehow and resolved via $PATH etc.
     */
    public node(args: string[], keepAlive: boolean = false): void {
        //document.getElementById("stdout")!.textContent = "";
        //document.getElementById("stderr")!.textContent = "";
        //this.terminal.clear();
        const vm = this;
        const worker = new Worker("/bin/node/app.js");
        if (keepAlive) (self as any)._keepAlive = worker;
        worker.onmessage = function (ev: MessageEvent) { const { f, x } = ev.data; vm.syscall(this, f, x); };
        // worker.onerror = function (ev: ErrorEvent) { console.error(JSON.stringify(ev, null, 2)); };
        const env: Environment = { fs: this.fs, cwd: "/cwd" };
        worker.postMessage({ type: "start", args, env });

        // @ts-ignore
        this.process.stdin.onData((ch: string) => {

            if (ch.includes('process.exit')) {
                this.process.exit()
                return
            }
            // {
            //     "type": "stdin",
            //     "ch": "\r",
            //     "key": {
            //         "name": "enter",
            //         "ctrl": false,
            //         "shift": false,
            //         "meta": false,
            //         "alt": false
            //     }
            // }

            // ch.split("").forEach(str => {
            //     //console.log(str.charCodeAt(0))
            //     if (str.charCodeAt(0) == 10) {
            //         worker.postMessage({
            //             type: "stdin",
            //             ch: "\r",
            //             key: {
            //                 alt: false,
            //                 ctrl: false,
            //                 meta: false,
            //                 name: 'enter',
            //                 shift: false
            //             }
            //         });
            //     } else {
            //         worker.postMessage({
            //             type: "stdin",
            //             ch: str,
            //             key: {
            //                 alt: false,
            //                 ctrl: false,
            //                 meta: false,
            //                 name: str,
            //                 shift: false
            //             }
            //         });
            //     }

            // })
            //if (ch.length > 8) { // assume paste (TODO: clean, see VSCode recent developments)
            worker.postMessage({
                type: "stdin",
                ch: ch,
                key: {
                    name: ch,
                    ctrl: false,
                    shift: false,
                    meta: false,
                    alt: false
                }
            });
            //}
            // if (ch.length === 1) {
            //     switch (ch.charCodeAt(0)) {
            //         case 3: // Ctrl + C
            //             break;
            //         case 22: // Ctrl + V
            //             break;
            //     }
            // }
        });


        // TODO: 需要接管 xterm.onKey, 不然输入会打印两次
        // term.onKey((key) => {
        //     console.log('测试')
        // })
    }
}

export default function main(argv: string[], process: IProcessModule) {
    new VirtualMachine({}, process).node([], false)
    //process.exit();
}